﻿using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using StudyCompanion.Commons.Authorize;
using StudyCompanion.Commons.Cache;
using StudyCompanion.Commons.Converter;
using StudyCompanion.Commons.WebSocket;
using StudyCompanion.Core.Share;

namespace StudyCompanion.Core.Extensions;

public static class ServiceCollectionExtension
{
    /// <summary>
    ///     添加缓存
    /// </summary>
    /// <param name="services"></param>
    /// <exception cref="ArgumentNullException"></exception>
    public static void AddCache(this IServiceCollection services)
    {
        var config = services.BuildServiceProvider().GetService<IConfiguration>();
        var d = config.GetValue(typeof(CacheOption), "Cache");
        var cacheOption = new CacheOption
        {
            IsUseRedis = config.GetValue<bool>("Cache:IsUseRedis"),
            ConnectionString = config["Cache:ConnectionString"],
            InstanceName = config["Cache:InstanceName"]
        };

        var options = new JsonSerializerOptions
        {
            Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
            WriteIndented = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            AllowTrailingCommas = true
        };
        //设置时间格式
        options.Converters.Add(new DateTimeJsonConverter());
        options.Converters.Add(new DateTimeNullableJsonConverter());
        //设置bool获取格式
        options.Converters.Add(new BooleanJsonConverter());
        //设置数字
        options.Converters.Add(new IntJsonConverter());
        options.Converters.Add(new LongJsonConverter());
        options.PropertyNameCaseInsensitive = true; //忽略大小写 

        //判断是否使用Redis，如果不使用 Redis就默认使用 MemoryCache
        if (cacheOption.IsUseRedis)
        {
            //Use Redis
            services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = cacheOption.ConnectionString;
                options.InstanceName = cacheOption.InstanceName;
            });
            services.AddSingleton(typeof(ICacheService), new RedisCacheService(new RedisCacheOptions
            {
                Configuration = cacheOption.ConnectionString,
                InstanceName = cacheOption.InstanceName
            }, options));
            services.Configure<DistributedCacheEntryOptions>(option =>
                option.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)); //设置Redis缓存有效时间为5分钟。
        }
        else
        {
            services.AddSingleton<IMemoryCache>(factory =>
            {
                var cache = new MemoryCache(new MemoryCacheOptions());
                return cache;
            });
            services.AddSingleton<ICacheService, MemoryCacheService>();
            services.Configure<MemoryCacheEntryOptions>(
                options => options.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)); //设置MemoryCache缓存有效时间为5分钟
            services.AddMemoryCache(); // 启用MemoryCache
        }

        services.AddSingleton(cacheOption);
    }

    /// <summary>
    ///     添加认证
    /// </summary>
    /// <param name="services"></param>
    /// <exception cref="ArgumentNullException"></exception>
    public static void AddJWTAuthorization(this IServiceCollection services)
    {
        if (services == null) throw new ArgumentNullException(nameof(services));
        var jwtConfig = services.BuildServiceProvider().GetService<IConfiguration>().GetSection("Jwt");
        var jwtOption = new JwtOption
        {
            Issuer = jwtConfig["Issuer"],
            Expire = Convert.ToInt16(jwtConfig["Expiration"]),
            Secret = jwtConfig["Secret"],
            Audience = jwtConfig["Audience"]
        };
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            ;
        }).AddJwtBearer(jwtBearerOptions =>
        {
            jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOption.Secret)), //秘钥
                ValidateIssuer = true,
                ValidIssuer = jwtOption.Issuer,
                ValidateAudience = true,
                ValidAudience = jwtOption.Audience,
                ValidateLifetime = true,
                ClockSkew = TimeSpan.FromMinutes(5)
            };

            jwtBearerOptions.Events = new JwtBearerEvents
            {
                OnMessageReceived = context =>
                {
                    var accessToken = context.Request.Query["access_token"];

                    var path = context.HttpContext.Request.Path;
                    if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/msg"))
                        context.Token = accessToken;
                    return Task.CompletedTask;
                },
                //在Token验证通过后调用
                OnTokenValidated = context =>
                {
                    var userContext = context.HttpContext.RequestServices.GetService<IUserContext>();
                    var claims = context.Principal.Claims;
                    userContext.Id = long.Parse(claims.First(x => x.Type == ClaimConst.UserId).Value);
                    userContext.ClientId = claims.First(x => x.Type == ClaimConst.ClientId).Value;
                    userContext.RemoteIpAddress = context.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
                    return Task.CompletedTask;
                },
                //认证失败时调用
                OnAuthenticationFailed = context =>
                {
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        context.Response.Headers.Add("Token-Expired", "true");
                    return Task.CompletedTask;
                },
                //未授权时调用
                OnChallenge = context =>
                {
                    context.Response.Headers.Add("Token-Error", context.ErrorDescription);
                    return Task.CompletedTask;
                }
            };
        });
        services.Configure<JwtOption>(jwtConfig); //注册配置
    }

    /// <summary>
    ///     添加websocket
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceCollection AddWebSocketManager(this IServiceCollection services)
    {
        services.AddTransient<WebSocketConnectionManager>();
        services.AddSingleton(typeof(WebSocketHandler));

        return services;
    }
}